package org.marsdonne.commons;

import com.github.oxo42.stateless4j.StateMachine;
import com.github.oxo42.stateless4j.StateMachineConfig;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.Transient;

/**
 * 可执行任务基类
 */
@MappedSuperclass
public abstract class ExecutableJob extends VersionedIdentifiableEntity {

    @Column(nullable = false)
    private JobState state;

    public void setState(JobState state) {
        this.state = state;
        this.initStateMachine();
    }

    public JobState getState() {
        return state;
    }

    @Transient
    private final StateMachineConfig<JobState, JobTrigger> STATE_MACHINE_CONFIG = new StateMachineConfig<>();
    @Transient
    private StateMachine<JobState, JobTrigger> stateMachine;

    protected void handleApproval() {
    }
    protected void handleInspect() {
    }
    protected void handleRejection() {
    }
    protected void handleCancellation() {
    }

    public ExecutableJob() {
        this.state = JobState.Planned;

        STATE_MACHINE_CONFIG.configure(JobState.Planned)
                .permit(JobTrigger.Approve, JobState.Approved)
                .permit(JobTrigger.Reject, JobState.Rejected)
                .permit(JobTrigger.Cancel, JobState.Cancelled);

        STATE_MACHINE_CONFIG.configure(JobState.Approved)
                .onEntry(this::handleApproval)
                .permit(JobTrigger.Cancel, JobState.Cancelled);

        STATE_MACHINE_CONFIG.configure(JobState.Rejected)
                .onEntry(this::handleRejection)
                .permit(JobTrigger.Apply, JobState.Planned);

        STATE_MACHINE_CONFIG.configure(JobState.Cancelled)
                .onEntry(this::handleCancellation)
                .permit(JobTrigger.Apply, JobState.Planned);

        STATE_MACHINE_CONFIG.configure(JobState.Approved)
                .onEntry(this::handleInspect)
                .permit(JobTrigger.Inspect, JobState.Completed);

        this.initStateMachine();
    }

    @PostLoad
    public void initStateMachine() {
        this.stateMachine = new StateMachine<>(this.state, this.STATE_MACHINE_CONFIG);
    }

    public boolean process(JobTrigger trigger) {
        boolean result = this.stateMachine.canFire(trigger);
        if (result) {
            this.stateMachine.fire(trigger);
            this.state = this.stateMachine.getState();
        }
        return result;
    }
}
